﻿namespace EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors
{
	/// <summary>
	/// Supervisor session
	/// </summary>
	internal class Monitoring : EFS.RC.CC.Sessions.OfficeService.Participant
	{
		#region Composition

		// Operator
		private EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence = null;

		// context channel 
		private EFS.RC.CC.Channels.Contexts.Operators.Operator contextChannel = null;

		// Supervisor monitoring call (first leg of B2B)
		private Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall inboundSupervisorAudioVideoCall = null;

		// office call (second leg of B2B)
		private Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall officeConversationAudioVideoCall = null;

		// Back to back call into office conversation
		private Microsoft.Rtc.Collaboration.BackToBackCall officeConversationSupervisorB2BCall = null;

		// Customer session
		private EFS.RC.CC.Confereces.OfficeService officeServiceConference = null;

		// Supervisor conversation
		private Microsoft.Rtc.Collaboration.Conversation supervisorConversation = null;

		/// <summary>
		/// Status
		/// </summary>
		public enum SessionStatus { Idle, Started, Terminated }

		/// <summary>
		/// Status
		/// </summary>
		public enum MonitoringStatus { None, Listen, Whisper, BargeIn }

		/// <summary>
		/// Status
		/// </summary>
		private SessionStatus status = SessionStatus.Idle;
		
		/// Monitoring
		private MonitoringStatus currentMonitoringStatus = MonitoringStatus.None;
		private MonitoringStatus transitMonitoringStatus = MonitoringStatus.None;

		#endregion

		#region Delegates

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="supervisorPresence"></param>
		public Monitoring(EFS.RC.CC.Confereces.OfficeService officeServiceConference, EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence)
		{
			// check
			EFS.Common.Verify.Reference(officeServiceConference, "officeServiceConference");
			EFS.Common.Verify.Reference(supervisorPresence, "supervisorPresence");

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(officeServiceConference.CustomerSession.ConversationGuid, "Monitoring:ctor", supervisorPresence.User.SIP, GetType());

			// set internals
			this.officeServiceConference = officeServiceConference;
			this.supervisorPresence = supervisorPresence;

			// register for conference terminate
			officeServiceConference.AsinkTerminatedDelegate.Register(OnOfficeServiceConferenceTerminated);
		}

		#endregion

		#region Usage

		/// <summary>
		/// Supervisor inbound call
		/// </summary>
		public Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall InboundSupervisorAudioVideoCall
		{
			get { return inboundSupervisorAudioVideoCall; }
		}

		/// <summary>
		/// Get operator
		/// </summary>
		public EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor SupervisorPresence
		{
			get { return supervisorPresence; }
		}

		/// <summary>
		/// Impersonated uri
		/// </summary>
		public override System.String ParticipantEndPointUri
		{
			get { return supervisorPresence.User.SIP; }
		}

		/// <summary>
		/// Office service conference
		/// </summary>
		public override EFS.RC.CC.Confereces.OfficeService OfficeServiceConference
		{
			get { return officeServiceConference; }
		}

		/// <summary>
		/// Describe this object into a nice formated string
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring [{0}] [{1}]", supervisorPresence.Describe(), officeServiceConference.Describe());
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public override EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start end point
			return BeginAsyncProcess2(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Shutdown monitoring session
		/// </summary>
		public override EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start end point
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		/// <summary>
		/// Begin wrap
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginWrap()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// start end point
			return BeginAsyncProcess2(Wrap);
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginHold()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginHold", this);

			// start end point
			return BeginAsyncProcess2(Hold);
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginReset()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginHold", this);

			// start end point
			return BeginAsyncProcess2(Reset);
		}

		/// <summary>
		/// Handle incomming call from conference
		/// </summary>
		/// <param name="call"></param>
		internal EFS.Common.Async.AsyncProcess HandleIncommingCall(Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall audioVideoCall)
		{
			// check
			EFS.Common.Verify.Reference(audioVideoCall, "audioVideoCall");

			// check not already handled
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check
				if (null != inboundSupervisorAudioVideoCall)
				{
					throw new System.InvalidOperationException("Conference call already handled");
				}

				// keep;
				inboundSupervisorAudioVideoCall = audioVideoCall;
			}

			// make B2B 
			return BeginAsyncProcess2(MakeB2BCall, OnMakeB2BCallComplete);
		}

		/// Status
		public SessionStatus Status { get { return status; } }

		/// Status
		public MonitoringStatus CurrentMonitoringStatus { get { return currentMonitoringStatus; } }

		#endregion

		#region Startup

		/// <summary>
		/// Session startup
		/// </summary>
		private void Startup()
		{
			// log
			EFS.EventLog.Log.LogInfo("Startup", this);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (status != SessionStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle Monitoring Session can be started");
				}

				// started
				status = SessionStatus.Started;
			}

			// set supervisor to customer session
			officeServiceConference.SetSupervisorSession(this);

			// rtl
			EFS.RC.CC.Tools.Loggers.CHL.Add(officeServiceConference.CustomerSession.ConversationGuid, null, null, null, supervisorPresence.User.ObjectGuid, null, null, null, EFS.RC.CC.Events.Conversation.Monitor_Start);

			// call office conference
			BeginAsyncProcess2(JoinOfficeConference);
		}

		/// <summary>
		/// Join to office conference
		/// </summary>
		internal void JoinOfficeConference()
		{
			// get conference uri
			System.String conferenceUri = officeServiceConference.ConferenceUri;

			// get end point
			Microsoft.Rtc.Collaboration.ApplicationEndpoint applicationEndpoint = officeServiceConference.CustomerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint;

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// log impersonation
				EFS.EventLog.Log.LogInfo(System.String.Format("Impersonate supervisorConversation {0}", supervisorPresence.User.SIP), this);

				// make office conversation
				supervisorConversation = new Microsoft.Rtc.Collaboration.Conversation(applicationEndpoint);
                supervisorConversation.Impersonate(supervisorPresence.User.SIP, null, null);
				supervisorConversation.ApplicationContext = this;
				supervisorConversation.StateChanged += OnSupervisorConversationStateChanged;

				// conference options
				Microsoft.Rtc.Collaboration.ConferenceJoinOptions conferenceJoinOptions = new Microsoft.Rtc.Collaboration.ConferenceJoinOptions();
				conferenceJoinOptions.JoinMode = Microsoft.Rtc.Collaboration.JoinMode.TrustedParticipant;

				// join
				supervisorConversation.ConferenceSession.BeginJoin(
					conferenceUri,
					conferenceJoinOptions,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndJoinOfficeConference));
			}
		}

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		private void EndJoinOfficeConference(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndJoinOfficeConference", this);

			try
			{
				// end join
				supervisorConversation.ConferenceSession.EndJoin(ar);

				// make customer backtoback call into the office conversation
				BeginAsyncProcess2(CallOfficeConference);
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Dial into office conference
		/// </summary>
		private void CallOfficeConference()
		{
			EFS.EventLog.Log.LogInfo("CallOfficeConference", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// dial out option
				Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuDialOutOptions options = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuDialOutOptions();
				options.PrivateAssistantDisabled = true;
				options.RemoveFromDefaultRouting = true;
				options.Media.Add(new Microsoft.Rtc.Collaboration.McuMediaChannel(Microsoft.Rtc.Collaboration.MediaType.Audio, Microsoft.Rtc.Collaboration.McuMediaChannelStatus.SendReceive));
				options.ParticipantUri = supervisorConversation.LocalParticipant.Uri;
				options.ParticipantDisplayName = "Customer Conference";

				// dial out
				supervisorConversation.ConferenceSession.AudioVideoMcuSession.BeginDialOut(
					supervisorConversation.Endpoint.EndpointUri,
					options,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndDialOut));
			}
		}

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		private void EndDialOut(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndDialOut", this);

			try
			{
				// check
				supervisorConversation.ConferenceSession.AudioVideoMcuSession.EndDialOut(ar);
			}
			catch (System.Exception ex)
			{
				EFS.EventLog.Log.LogException(ex, this);

				// shut it down
				BeginShutdown();
			}
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnStartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// shut it down
				BeginShutdown();
			}
		}

		/// <summary>
		/// Make B2B call
		/// </summary>
		/// <param name="call"></param>
		private void MakeB2BCall()
		{
			EFS.EventLog.Log.LogInfo("MakeB2BCall", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// Make B2B
				Microsoft.Rtc.Collaboration.Conversation officeConversation = new Microsoft.Rtc.Collaboration.Conversation(inboundSupervisorAudioVideoCall.Conversation.Endpoint);
				officeConversationAudioVideoCall = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall(officeConversation);

				// office call settings
				Microsoft.Rtc.Collaboration.BackToBackCallSettings officeConversationB2BCallSettings = new Microsoft.Rtc.Collaboration.BackToBackCallSettings(officeConversationAudioVideoCall, supervisorPresence.User.SIP);
				Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCallEstablishOptions officeConversationB2BCallOptions = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCallEstablishOptions();
				officeConversationB2BCallSettings.CallEstablishOptions = officeConversationB2BCallOptions;

				// office call settings
				Microsoft.Rtc.Collaboration.BackToBackCallSettings inboundB2BCallSettings = new Microsoft.Rtc.Collaboration.BackToBackCallSettings(inboundSupervisorAudioVideoCall);

				// make b2b call
				officeConversationSupervisorB2BCall = new Microsoft.Rtc.Collaboration.BackToBackCall(
					officeConversationB2BCallSettings,
					inboundB2BCallSettings);

				// establish
				officeConversationSupervisorB2BCall.BeginEstablish(
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(OnEndEstablishB2BCall));
			}
		}

		/// <summary>
		/// End modify role request
		/// </summary>
		/// <param name="ar"></param>
		private void OnEndEstablishB2BCall(System.IAsyncResult ar)
		{
			try
			{
				// check
				officeConversationSupervisorB2BCall.EndEstablish(ar);

				// established
				AsinkEstablishedDelegate.Start();

				// make private audio channel for listening
				TransitToStatus(MonitoringStatus.Listen);
			}
			catch (System.Exception ex)
			{
				// on exception
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// MakeB2BCall complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnMakeB2BCallComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnMakeB2BCallComplete", this);

			try
			{
				// check
				ap.Throw();
			}
			catch (System.Exception ex)
			{
				// log
				HandleExceptionSafe(ex);
			}
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(officeServiceConference.CustomerSession.ConversationGuid, "OnException", ex, this);

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// Called when participant end point added
		/// </summary>
		protected override void ParticipantEndPointAdded()
		{
			// log
			EFS.EventLog.Log.LogInfo("Monitoring: ParticipantEndPointAdded", this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(officeServiceConference.CustomerSession.ConversationGuid, "ParticipantEndPointAdded", this);

			// make my channel
			BeginAsyncProcess2(InitContextChannel);
		}

		/// <summary>
		/// Init context
		/// </summary>
		/// <param name="conversation"></param>
		/// <param name="remoteEndPoint"></param>
		protected void InitContextChannel()
		{
			// log
			EFS.EventLog.Log.LogInfo("InitContextChannel", this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(officeServiceConference.CustomerSession.ConversationGuid, "InitContextChannel", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null != contextChannel)
				{
					throw new System.InvalidOperationException("Context Channel already Initialized");
				}

				// make context
				contextChannel = new Channels.Contexts.Operators.Operator("supervisor", officeConversationAudioVideoCall.Conversation, officeConversationAudioVideoCall.RemoteEndpoint, OnContextEstablished, OnContextMessageReceived);
				contextChannel.BeginStartup();
			}
		}

		/// <summary>
		/// Called when participant end point removed
		/// </summary>
		protected override void ParticipantEndPointRemoved()
		{
			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// Wrap
		/// </summary>
		private void Wrap()
		{
			// log
			EFS.EventLog.Log.LogInfo("Wrap", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null != contextChannel)
				{
					contextChannel.BeginDataSend(new EFS.RC.CC.Messages.Communicator.Lync.Conversation.WrapWarning());
				}
			}
		}

		/// <summary>
		/// Hold
		/// </summary>
		private void Hold()
		{
			// log
			EFS.EventLog.Log.LogInfo("Hold", this);

			// go to wisper if on hold
			lock (syncAccess)
			{
				if (transitMonitoringStatus == MonitoringStatus.BargeIn || currentMonitoringStatus == MonitoringStatus.BargeIn)
				{
					// move to wisper
					TransitToStatus(MonitoringStatus.Whisper);
				}
			}
		}

		/// <summary>
		/// Hold
		/// </summary>
		private void Reset()
		{
			// log
			EFS.EventLog.Log.LogInfo("Reset", this);

			// go to wisper if on hold
			lock (syncAccess)
			{
				// check
				if (transitMonitoringStatus == currentMonitoringStatus)
				{
					// keep current
					MonitoringStatus resetMonitoringStats = currentMonitoringStatus;

					// reset current 
					transitMonitoringStatus = MonitoringStatus.None;
					currentMonitoringStatus = MonitoringStatus.None;

					// move to wisper
					TransitToStatus(resetMonitoringStats);
				}
			}
		}

		/// <summary>
		/// Customer session terminated
		/// </summary>
		/// <param name="agentHunter"></param>
		private void OnOfficeServiceConferenceTerminated(EFS.RC.CC.Confereces.OfficeService officeServiceConference)
		{
			// check
			EFS.Common.Verify.Reference(officeServiceConference, "officeServiceConference");

			// check mine
			EFS.Common.Verify.Condition(this.officeServiceConference.Equals(officeServiceConference), "Terminated Office Service Conference is different");

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// Make listening routes for supervisor
		/// </summary>
		private void StartListening()
		{
			// get participants
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> listOfParticipantEndpoints = officeServiceConference.GetRemoteParticipantEndpoints();

			// incomming routes
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute> incomingAudioRoutes =
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute>();

			// ouging routes
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute> outgoingAudioRoutes =
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute>();

			// find customer end point and decide routes
			foreach (Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint in listOfParticipantEndpoints)
			{
				// only authorized participants
				if (officeServiceConference.IsOperator(participantEndpoint.Participant.Uri) || officeServiceConference.IsCustomer(participantEndpoint.Participant.Uri))
				{
					// make incomming route
					Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute incomingRoute = new Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute(participantEndpoint);
					incomingRoute.IsDtmfEnabled = true;
					incomingRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					incomingAudioRoutes.Add(incomingRoute);

					// make outgoing route 
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Remove;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
				else if (officeServiceConference.IsRecorder(participantEndpoint.Participant.Uri))
				{
					// make outgoing route
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Remove;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
			}

			// check
			if (0 == outgoingAudioRoutes.Count && 0 == incomingAudioRoutes.Count)
			{
				return;
			}

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// update routes
				inboundSupervisorAudioVideoCall.AudioVideoMcuRouting.BeginUpdateAudioRoutes(
					outgoingAudioRoutes,
					incomingAudioRoutes,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndUpdateAudioRoutes, MonitoringStatus.Listen));
			}
		}

		/// <summary>
		/// Make wisper routes for supervisor
		/// </summary>
		private void StartWisper()
		{
			// get participants
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> listOfParticipantEndpoints = officeServiceConference.GetRemoteParticipantEndpoints();

			// incomming routes
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute> incomingAudioRoutes =
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute>();

			// ouging routes
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute> outgoingAudioRoutes =
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute>();

			// find customer end point and decide routes
			foreach (Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint in listOfParticipantEndpoints)
			{
				// only authorized participants
				if (officeServiceConference.IsCustomer(participantEndpoint.Participant.Uri))
				{
					// make incomming route
					Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute incomingRoute = new Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute(participantEndpoint);
					incomingRoute.IsDtmfEnabled = true;
					incomingRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					incomingAudioRoutes.Add(incomingRoute);

					// make outgoing route
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Remove;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
				else if (officeServiceConference.IsOperator(participantEndpoint.Participant.Uri))
				{
					// make incomming route
					Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute incomingRoute = new Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute(participantEndpoint);
					incomingRoute.IsDtmfEnabled = true;
					incomingRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					incomingAudioRoutes.Add(incomingRoute);

					// make outgoing route
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
				else if (officeServiceConference.IsRecorder(participantEndpoint.Participant.Uri))
				{
					// make outgoing route
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
			}

			// check
			if (0 == outgoingAudioRoutes.Count && 0 == incomingAudioRoutes.Count)
			{
				return;
			}

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// update
				inboundSupervisorAudioVideoCall.AudioVideoMcuRouting.BeginUpdateAudioRoutes(
					outgoingAudioRoutes,
					incomingAudioRoutes,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndUpdateAudioRoutes, MonitoringStatus.Whisper));
			}
		}

		/// <summary>
		/// Make barge-in routes for supervisor
		/// </summary>
		private void StartBargeIn()
		{
			// get participants
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> listOfParticipantEndpoints = officeServiceConference.GetRemoteParticipantEndpoints();

			// incomming routes
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute> incomingAudioRoutes =
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute>();

			// ouging routes
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute> outgoingAudioRoutes =
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute>();

			// find customer end point and decide routes
			foreach (Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint in listOfParticipantEndpoints)
			{
				// only authorized participants
				if (officeServiceConference.IsCustomer(participantEndpoint.Participant.Uri) || officeServiceConference.IsOperator(participantEndpoint.Participant.Uri))
				{
					// make incomming route
					Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute incomingRoute = new Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute(participantEndpoint);
					incomingRoute.IsDtmfEnabled = true;
					incomingRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					incomingAudioRoutes.Add(incomingRoute);

					// make outgoing route
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
				else if (officeServiceConference.IsRecorder(participantEndpoint.Participant.Uri))
				{
					// make outgoing route
					Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingAudioRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(participantEndpoint);
					outgoingAudioRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

					// add
					outgoingAudioRoutes.Add(outgoingAudioRoute);
				}
			}

			// check
			if (0 == outgoingAudioRoutes.Count && 0 == incomingAudioRoutes.Count)
			{
				return;
			}

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// update
				inboundSupervisorAudioVideoCall.AudioVideoMcuRouting.BeginUpdateAudioRoutes(
					outgoingAudioRoutes,
					incomingAudioRoutes,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndUpdateAudioRoutes, MonitoringStatus.BargeIn));
			}
		}

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		private void EndUpdateAudioRoutes(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndUpdateAudioRoutes", this);

			try
			{
				// try 
				inboundSupervisorAudioVideoCall.AudioVideoMcuRouting.EndUpdateAudioRoutes(ar);

				// get state delegate
				EFS.Common.Async.AsyncResult.StateDelegate stateDelegate = (ar.AsyncState as EFS.Common.Async.AsyncResult.StateDelegate);
				EFS.Common.Verify.Reference(stateDelegate, "stateDelegate");

				// get pass trough paramter
				MonitoringStatus? monitoringStatus = (stateDelegate.PassTrough as MonitoringStatus?);
				EFS.Common.Verify.Reference(monitoringStatus, "monitoringStatus");

				// set new status
				CompletedStatusTransit(monitoringStatus.Value);
			}
			catch (System.Exception ex)
			{
				// handle
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// completed status tranit
		/// </summary>
		private void CompletedStatusTransit(MonitoringStatus newMonitoringStatus)
		{
			EFS.EventLog.Log.LogInfo("CompletedStatusTransit", this);

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check transit
				if (transitMonitoringStatus != newMonitoringStatus)
				{
					// set final status
					currentMonitoringStatus = newMonitoringStatus;

					// Status has changed already
					ApplyStatusTransit(transitMonitoringStatus);
				}
				else
				{
					// set final status
					currentMonitoringStatus = transitMonitoringStatus;

					// log
					switch (newMonitoringStatus)
					{
						case MonitoringStatus.Listen:
							{
								// rtl
								EFS.RC.CC.Tools.Loggers.CHL.Add(officeServiceConference.CustomerSession.ConversationGuid, null, null, null, supervisorPresence.User.ObjectGuid, null, null, null, EFS.RC.CC.Events.Conversation.Monitor_Listen);
								break;
							}
						case MonitoringStatus.Whisper:
							{
								// rtl
								EFS.RC.CC.Tools.Loggers.CHL.Add(officeServiceConference.CustomerSession.ConversationGuid, null, null, null, supervisorPresence.User.ObjectGuid, null, null, null, EFS.RC.CC.Events.Conversation.Monitor_Whisper);
								break;
							}
						case MonitoringStatus.BargeIn:
							{
								// in case customer is on hold, get custmer back
								if (OfficeServiceConference.CustomerSession.OnHold)
								{
									// resume
									OfficeServiceConference.CustomerSession.Resume();
								}

								// rtl
								EFS.RC.CC.Tools.Loggers.CHL.Add(officeServiceConference.CustomerSession.ConversationGuid, null, null, null, supervisorPresence.User.ObjectGuid, null, null, null, EFS.RC.CC.Events.Conversation.Monitor_BargeIn);
								break;
							}
					}
				}
			}
		}

		/// <summary>
		/// Handle supervisor control message
		/// </summary>
		/// <param name="message"></param>
		private void OnContextMessageReceived(EFS.RC.CC.Messages.Message message)
		{
			EFS.EventLog.Log.LogInfo("OnContextMessageReceived", this);

			// check
			EFS.Common.Verify.Reference(message, "message");

			try
			{
				// dispatch
				if (message is EFS.RC.CC.Messages.Supervisor.Tools.Monitor)
				{
					TransitToStatus(MonitoringStatus.Listen);
				}
				else if (message is EFS.RC.CC.Messages.Supervisor.Tools.Whisper)
				{
					TransitToStatus(MonitoringStatus.Whisper);
				}
				else if (message is EFS.RC.CC.Messages.Supervisor.Tools.BargeIn)
				{
					TransitToStatus(MonitoringStatus.BargeIn);
				}
				else if (message is EFS.RC.CC.Messages.Supervisor.Agent.Request)
				{
					officeServiceConference.SendOperatorMessage(message);
				}
				else if (message is EFS.RC.CC.Messages.Communicator.Lync.Conversation.Update)
				{
					// send status
					BeginAsyncProcess2(SendConversationStatusToConversationContext);
				}
				else if (message is EFS.RC.CC.Messages.Communicator.Lync.Conversation.Hold)
				{
					// hold
					officeServiceConference.BeginHoldCustomerConversation();
				}
				else if (message is EFS.RC.CC.Messages.Communicator.Lync.Conversation.Resume)
				{
					// hold
					officeServiceConference.BeginResumeCustomerConversation();
				}
				else if (message is EFS.RC.CC.Messages.Communicator.Lync.Context.Aplication)
				{
					// get app
					EFS.RC.CC.Messages.Communicator.Lync.Context.Aplication contextApplication = message as EFS.RC.CC.Messages.Communicator.Lync.Context.Aplication;

					// check
					if (contextApplication.Context != "supervisor")
					{
						throw new System.InvalidOperationException(System.String.Format("Supervisor context expected but received {0}", contextApplication.Context));
					}

					// send participants and conversation status
					BeginAsyncProcess2(SendCallerToConversationContext);
					BeginAsyncProcess2(SendParticipantsToConversationContext);
					BeginAsyncProcess2(SendConversationStatusToConversationContext);
				}
			}
			catch (System.Exception ex)
			{
				// handle
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Set new status
		/// </summary>
		/// <param name="status"></param>
		private void TransitToStatus(MonitoringStatus newMonitoringStatus)
		{
			EFS.EventLog.Log.LogInfo("TransitToStatus", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check current status
				if (transitMonitoringStatus != currentMonitoringStatus)
				{
					// we are in transit, just set new transit
					transitMonitoringStatus = newMonitoringStatus;
				}
				else
				{
					// set
					transitMonitoringStatus = newMonitoringStatus;

					// manage transit
					ApplyStatusTransit(newMonitoringStatus);
				}
			}
		}

		/// <summary>
		/// Apply status tr
		/// </summary>
		/// <param name="newMonitoringStatus"></param>
		private void ApplyStatusTransit(MonitoringStatus newMonitoringStatus)
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check ne wstatus
				switch (newMonitoringStatus)
				{
					// listen
					case MonitoringStatus.Listen:
						{
							// check
							if (currentMonitoringStatus != MonitoringStatus.Listen)
							{
								if (currentMonitoringStatus == MonitoringStatus.BargeIn)
								{
									// listen from Barge in transit via Whisper
									BeginAsyncProcess2(StartWisper);
								}
								else
								{
									// just go Listen
									BeginAsyncProcess2(StartListening);
								}
							}

							// break
							break;
						}
					// barge in
					case MonitoringStatus.BargeIn:
						{
							// check
							if (currentMonitoringStatus != MonitoringStatus.BargeIn)
							{
								// Barge in
								BeginAsyncProcess2(StartBargeIn);
							}

							// break
							break;
						}
					// whisper
					case MonitoringStatus.Whisper:
						{
							// check
							if (currentMonitoringStatus != MonitoringStatus.Whisper)
							{
								// Barge in
								BeginAsyncProcess2(StartWisper);
							}

							// break
							break;
						}
				}
			}
		}

		/// <summary>
		/// Changes to supervisor conversation
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnSupervisorConversationStateChanged(object sender, Microsoft.Rtc.Signaling.StateChangedEventArgs<Microsoft.Rtc.Collaboration.ConversationState> args)
		{
			// log
			EFS.EventLog.Log.LogInfo(System.String.Format("OnSupervisorConversationStateChanged {0}", args.State), this);

			try
			{
				// SAL
				EFS.RC.CC.Tools.Loggers.SAL.Add(officeServiceConference.CustomerSession.ConversationGuid, "OnSupervisorConversationStateChanged", args.State, this);

				// manage
				switch (args.State)
				{
					case Microsoft.Rtc.Collaboration.ConversationState.Terminating:
						{
							// begin shut down
							BeginShutdown();
							break;
						}
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Context message received
		/// </summary>
		/// <param name="message"></param>
		public void OnContextEstablished(EFS.RC.CC.Channels.Contexts.Operators.Operator operatorContextChannel)
		{
			// check
			EFS.Common.Verify.Reference(operatorContextChannel, "operatorContextChannel");

			lock (syncAccess)
			{
				// check 
				if (!this.contextChannel.Equals(operatorContextChannel))
				{
					throw new System.InvalidOperationException("Context Channel established is different then own");
				}
			}
		}

		// send participants
		private void SendCallerToConversationContext()
		{
			// get message
			EFS.RC.CC.Messages.Customer.Caller callerCustomerMessage = officeServiceConference.CustomerSession.GetCallerInformationMessage();

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null != contextChannel)
				{
					// send info
					contextChannel.BeginDataSend(callerCustomerMessage);
				}
			}
		}

		/// <summary>
		/// Send participants into conversation context
		/// </summary>
		/// <param name="message"></param>
		private void SendParticipantsToConversationContext()
		{
			// send this info
			EFS.RC.CC.Messages.Communicator.Lync.Participant.StatusList participantStatusList = officeServiceConference.GetParticipanthStatusListMessage(true);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null != contextChannel)
				{
					// send info
					contextChannel.BeginDataSend(participantStatusList);
				}
			}
		}

		/// <summary>
		/// Send status into conversation context
		/// </summary>
		/// <param name="message"></param>
		private void SendConversationStatusToConversationContext()
		{
			// send this info
			EFS.RC.CC.Messages.Communicator.Lync.Conversation.Info conversationInfo = officeServiceConference.GetConversationInfoMessage();

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != SessionStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null != contextChannel)
				{
					// send info
					contextChannel.BeginDataSend(conversationInfo);
				}
			}
		}

		#endregion // Runtime

		#region Shutdown

		/// <summary>
		/// Shut down
		/// </summary>
		public void Shutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("Shutdown", this);

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check already terminated
				if (status != SessionStatus.Terminated)
				{
					// set terinated
					status = SessionStatus.Terminated;
					transitMonitoringStatus = MonitoringStatus.None;

					// notify
					AsinkTerminatedDelegate.Start();
				}
				else
				{
					// already terminated
					return;
				}
			}

			// rtl
			EFS.RC.CC.Tools.Loggers.CHL.Add(officeServiceConference.CustomerSession.ConversationGuid, null, null, null, supervisorPresence.User.ObjectGuid, null, null, null, EFS.RC.CC.Events.Conversation.Monitor_Terminate);

			// make termination
			EFS.RC.CC.Tools.Functions.Termination termination = new EFS.RC.CC.Tools.Functions.Termination(this);

			// remove delegate
			supervisorConversation.StateChanged -= OnSupervisorConversationStateChanged;

			// terminate all
			termination.Terminate(officeConversationSupervisorB2BCall);
			
			// close context
			CloseContextChannel();
		}

		/// <summary>
		/// Close context
		/// </summary>
		private void CloseContextChannel()
		{
			// log
			EFS.EventLog.Log.LogInfo("CloseContextChannel", this);

			try
			{
				// close channel
				if (null != contextChannel)
				{
					// terminate channel
					contextChannel.BeginShutdown();
				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnShutdownComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion
	}
}

